#pragma once

#ifdef CONFIG_CORO_USE_TEST_TIMER
#include "pw_async2/simulated_time_provider.h"
#endif

#include "pw_async2/time_provider.h"
#include "pw_chrono/system_clock.h"
#include "pw_function/function.h"
#include <chrono>
#include <zephyr/kernel.h>

namespace coro {
class Timer final : public pw::async2::TimeProvider<pw::chrono::SystemClock> {
public:
  Timer() {
    k_work_init_delayable(&m_item.m_dwork, [](k_work *item) {
      k_work_delayable *dwork = k_work_delayable_from_work(item);
      auto m_item = reinterpret_cast<Item *>(dwork);
      m_item->m_callback(m_item->m_deadline);
    });
    m_item.m_callback = [this](pw::chrono::SystemClock::time_point deadline) {
      TimeProvider::RunExpired(deadline);
    };
  }

private:
  struct Item {
    k_work_delayable m_dwork;
    pw::Function<void(pw::chrono::SystemClock::time_point deadline)> m_callback;
    pw::chrono::SystemClock::time_point m_deadline;
    k_work_sync m_sync;
  } m_item;
  pw::chrono::SystemClock::time_point now() final {
    return pw::chrono::SystemClock::now();
  }

  void DoInvokeAt(pw::chrono::SystemClock::time_point time_point) final {
    const auto duration = time_point - pw::chrono::SystemClock::now();
    m_item.m_deadline = time_point;
    k_work_schedule(&m_item.m_dwork, K_TICKS(duration.count()));
  }

  void DoCancel() final {
    k_work_cancel_delayable_sync(&m_item.m_dwork, &m_item.m_sync);
  }
};


} // namespace coro